#pragma once

#include <vector>
#include <mutex>
#include "Sem.hpp"

template<class Type>
class RingQueue
{
    static const int defaultNumber = 10;
    
public:

    RingQueue(int capacity = defaultNumber)
        :_queue(capacity)
        ,_capacity(capacity)
        ,_p_index(0)
        ,_c_index(0)
        ,_p_sem(capacity)       // 此时 _queue 队列为空，生产者信号量为 capacity ，表示可以生产的数量
        ,_c_sem(0)              // 此时 _queue 队列为空，不可消费
    {
        ;
    }

    template<class T>
    void push(T&& data)
    {
        _p_sem.wait();      // 获取信号量，并让总信号量 - 1，如果为 0 ，之后的线程会阻塞

        _p_mutex.lock();    // 获取信号量函数是原子操作，不用担心线程问题，可以先让线程们抢信号量，再加锁进入共享区

        _queue[_p_index] = data;
        std::cout << " 生产了一个商品，编号: " << data << std::endl;
        ++data;

        _p_index = (_p_index + 1) % _capacity;

        _p_mutex.unlock();  // 解锁

        _c_sem.post();      // 让消费者的总信号量 + 1，即可以认为唤醒消费者线程
    }

    Type pop()
    {
        _c_sem.wait();

        _c_mutex.lock();

        Type data = std::move(_queue[_c_index]);
        std::cout << " 消费了一个商品，编号: " << data << std::endl;

        _c_index = (_c_index + 1) % _capacity;

        _c_mutex.unlock();
        
        _p_sem.post();      // 让生产者的总信号量 + 1，即可以认为唤醒生产者线程

        return data;
    }

    ~RingQueue()
    {
        ;
    }

private:

    std::vector<Type> _queue;
    int _capacity;

    Sem _c_sem;
    int _c_index;

    Sem _p_sem;
    int _p_index;

    std::mutex _c_mutex;
    std::mutex _p_mutex;
};

